﻿using debugger;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;

using FTD2XX_NET;
using System.IO;
using System.Diagnostics;

namespace debugger
{
    public class Base : Form
    {
        public string BaseType = "ft232h";
        public int packageSize = 256;

        uint device = 0;

        public const byte VGA_CONTROL_BY_DEBUGGER = 0x06;
        public const byte VGA_DEBUGGER_READ_LINE_ADDR = 0x01;
        public const byte VGA_DEBUGGER_READ_LINE_REQ = 0x02;

        uint currPace = 0;


        public FTDI myFtdiDevice;

        [DllImport("CH341DLL.DLL")]
        public static extern IntPtr CH341OpenDevice(uint iIndex);

        [DllImport("CH341DLL.DLL")]
        public static extern void CH341CloseDevice(uint iIndex);

        [DllImport("CH341DLL.DLL")]
        public static extern bool CH341Set_D5_D0(uint iIndex, uint iSetDirOut, uint iSetDataOut);

        [DllImport("CH341DLL.DLL")]
        public static extern bool CH341StreamSPI4(uint iIndex, uint iChipSelect, uint iLength, byte[] ioBuffer);

        [DllImport("CH341DLL.DLL")]
        public static extern bool CH341SetStream(uint iIndex, uint iMode);

        [DllImport("CH341DLL.DLL")]
        public static extern bool CH341GetStatus(uint iIndex, ref uint iStatus);



        public bool connect(AfterConnect afterConnect)
        {


            IntPtr h = CH341OpenDevice(device);
            if (h.ToInt64() != 0)
            {
                //CH341ResetDevice(0);
                // 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz
                // 位2:     SPI的I/O数/IO引脚, 0=单入单出(D3时钟/D5出/D7入)(默认值),1=双入双出(D3时钟/D5出D4出/D7入D6入)
                // 位7:     SPI字节中的位顺序, 0=低位在前, 1=高位在前
                // 其它保留,必须为0
                //CH341SetStream(device, 0x83);
                CH341SetStream(device, 0x03);
                //CH341Set_D5_D0(device, MOSI_ON | SWITCH_OUT | SCK_ON | CS2_ON | CS1_ON | CS0_ON, SWITCH_OFF | SCK_HI | CS0_HI | CS1_HI | CS2_HI);
                //CH341Set_D5_D0(device, MOSI_OFF | SWITCH_IN | SCK_OFF | CS2_OFF | CS1_OFF | CS0_OFF, SWITCH_OFF | SCK_HI | CS0_HI | CS1_HI | CS2_HI);
                //CH341SetExclusive(device, 1);
            }
            else
            {
                CH341CloseDevice(device);
                return false;
            }


            //---------------------------------------------------------------------------------------------------------------------------------

            if (myFtdiDevice != null)
            {
                myFtdiDevice.Close();
            }
            else
            {
                myFtdiDevice = new FTDI();
            }
            FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;


            if (false)
            {
                UInt32 ftdiDeviceCount = 0;
                // Determine the number of FTDI devices connected to the machine
                ftStatus = myFtdiDevice.GetNumberOfDevices(ref ftdiDeviceCount);
                // Check status
                if (ftStatus == FTDI.FT_STATUS.FT_OK)
                {
                    Console.WriteLine("Number of FTDI devices: " + ftdiDeviceCount.ToString());
                    Console.WriteLine("");
                }
                else
                {
                    // Wait for a key press
                    Console.WriteLine("Failed to get number of devices (error " + ftStatus.ToString() + ")");
                    Console.ReadKey();
                    return false;
                }

                // If no devices available, return
                if (ftdiDeviceCount == 0)
                {
                    // Wait for a key press
                    Console.WriteLine("Failed to get number of devices (error " + ftStatus.ToString() + ")");
                    Console.ReadKey();
                    return false;
                }

                // Allocate storage for device info list
                FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

                // Populate our device list
                ftStatus = myFtdiDevice.GetDeviceList(ftdiDeviceList);

                if (ftStatus == FTDI.FT_STATUS.FT_OK)
                {
                    for (UInt32 i = 0; i < ftdiDeviceCount; i++)
                    {
                        Console.WriteLine("Device Index: " + i.ToString());
                        Console.WriteLine("Flags: " + String.Format("{0:x}", ftdiDeviceList[i].Flags));
                        Console.WriteLine("Type: " + ftdiDeviceList[i].Type.ToString());
                        Console.WriteLine("ID: " + String.Format("{0:x}", ftdiDeviceList[i].ID));
                        Console.WriteLine("Location ID: " + String.Format("{0:x}", ftdiDeviceList[i].LocId));
                        Console.WriteLine("Serial Number: " + ftdiDeviceList[i].SerialNumber.ToString());
                        Console.WriteLine("Description: " + ftdiDeviceList[i].Description.ToString());
                        Console.WriteLine("");
                    }
                }
                // Open first device in our list by serial number
            }



            ftStatus = myFtdiDevice.OpenByDescription("USB <-> Serial Converter");
            //ftStatus = myFtdiDevice.OpenBySerialNumber("FT7W0ENG");//ftdiDeviceList[0].SerialNumber
            //ftStatus = myFtdiDevice.OpenBySerialNumber("FT7VMXX5");//ftdiDeviceList[0].SerialNumber
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                // Wait for a key press
                Console.WriteLine("Failed to open device (error " + ftStatus.ToString() + ")");
                //Console.ReadKey();
                afterConnect();
                return true;
            }
            myFtdiDevice.ResetDevice();
            myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_RESET);
            myFtdiDevice.Purge(FTDI.FT_PURGE.FT_PURGE_RX | FTDI.FT_PURGE.FT_PURGE_TX);
            myFtdiDevice.SetTimeouts(2000, 2000);

            //myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_MPSSE);
            
            //ft_sync,debug_reset_n,avm_m1_waitrequest,halt_cpu,halt_debug,avm_m0_waitrequest,irq_enable,irq_req
            uint sync = sendCmdRet(0x27);

            if (((sync >> 7) & 1) != 0)
            {
                myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_SYNC_FIFO);
            }

            afterConnect();
            return true;
        }

        public delegate void AfterConnect();


        byte[] seq = new byte[1024];
        byte[] data = new byte[5];
        private uint readFromPort()
        {
            uint val = 0;
            //D0 cs0
            //D1 cs1
            //D2 cs2
            //D3 sck
            //D4 nc // switch
            //D5 mosi
            //D6 ack            //0x0080efb7            //0x0080eff7
            //D7 miso
            Stopwatch sw = new Stopwatch();
            sw.Start();
            while ((val & 0x40) == 0)
            {
                bool a = CH341GetStatus(device, ref val);
                if (!a)
                {
                    throw new Exception();
                }
                if (sw.ElapsedMilliseconds > 1000) {
                    Console.WriteLine("timeout");
                    return 0;
                }
                //Console.WriteLine("wait");
            }
            data[0] = 0;
            data[1] = 0;
            data[2] = 0;
            data[3] = 0;
            data[4] = 0;
            // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01/10分别选择D0/D1/D2引脚作为低电平有效片选
            bool ret = CH341StreamSPI4(device, 0x80, 4, data);
            uint temp = 0;
            temp |= data[0];
            temp |= (uint)(data[1] << 8);
            temp |= (uint)(data[2] << 16);
            temp |= (uint)(data[3] << 24);
            return temp;
        }

        public uint sendCmdRet(byte cmd, uint? udata = null)
        {
            sendCmd(cmd, udata);
            uint val = 0;
            val = readFromPort();
            return val;
        }


        static byte AC7 = 0x80;
        static byte NOE = 0x40;
        static byte CLKOUT = 0x20;
        static byte CMDDAT = 0x02;
        static byte OUTTOPC = 0x01;//out to pc
        static byte mask = (byte)(AC7 | CMDDAT | CLKOUT | OUTTOPC);//
        static byte _base = 0;//;


        public void sendCmd(byte cmd, uint? udata = null)
        {
            bool ret;

            data[0] = cmd;
            if (udata != null)
            {
                data[1] = (byte)(udata & 0xFF);
                data[2] = (byte)((udata >> 8) & 0xFF);
                data[3] = (byte)((udata >> 16) & 0xFF);
                data[4] = (byte)((udata >> 24) & 0xFF);
                //Console.WriteLine(Util.getHex2(cmd) + "," + Util.getHex8(udata.Value));
            }
            else
            {
                //Console.WriteLine(Util.getHex2(cmd) );
            }
            // 片选控制, 位7为0则忽略片选控制, 位7为1则参数有效: 位1位0为00/01/10分别选择D0/D1/D2引脚作为低电平有效片选
            ret = CH341StreamSPI4(device, 0x80, 5, data);
        }

        public void sendCmdNoRes(byte cmd, uint? udata = null)
        {
            sendCmd(cmd, udata);
        }

        uint written = 0;

        public bool receive(byte[] data, uint length)//read
        {
            //myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_SYNC_FIFO);
            //myFtdiDevice.Read(data, 0x20, ref written); 
            sendCmdNoRes(0x05);
            //myFtdiDevice.Read(data, 1, ref written);
            myFtdiDevice.Read(data, length, ref written); //written++;
            if (written != length)
            {
                Console.WriteLine("receive:" + written + "!=" + length);
            }
            uint aa = 0;
            myFtdiDevice.GetRxBytesAvailable(ref aa);
            //myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_MPSSE);
            if (written != length)
            {
                return false;
            }
            sendCmdNoRes(0x04);

            sendCmdNoRes(0x03);
            sendCmdNoRes(0x02);

            myFtdiDevice.Purge(FTDI.FT_PURGE.FT_PURGE_RX | FTDI.FT_PURGE.FT_PURGE_TX);
            return true;
        }
        public bool send(byte[] data, uint length)//write
        {
            //myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_SYNC_FIFO);

            sendCmdNoRes(0x05);
            myFtdiDevice.Write(data, length, ref written);
            //uint aa = 0;
            //myFtdiDevice.GetTxBytesWaiting(ref aa);
            
            //myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_MPSSE);
            if (written != length)
            {
                Console.WriteLine("send:" + written + "!=" + length);
                return false;
            }

            sendCmdNoRes(0x04);

            sendCmdNoRes(0x03);
            sendCmdNoRes(0x02);

            myFtdiDevice.Purge(FTDI.FT_PURGE.FT_PURGE_RX | FTDI.FT_PURGE.FT_PURGE_TX);
            return true;
        }
        public bool isBasic;
        public void getVGA(byte[] data_bmp, uint baseAddr, bool fast341, bool reset)
        {
            uint frameSize = 1024 * (1024 + 512);
            //frameSize = 1024 * 1024;

            sendCmd(Form1.SET_DEBUG_ADDRESS, baseAddr);
            sendCmd(Form1.SET_DEBUG_LENGTH, frameSize);
            sendCmd(Form1.VGA_TRANS);
            //receive(data_bmp, frameSize);

            sendCmdNoRes(0x05);

            //myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_SYNC_FIFO);

            myFtdiDevice.Read(data_bmp, frameSize, ref written);
            if (written != frameSize)
            {
                Console.WriteLine("vga:" + written + "!=" + frameSize);
            }
            myFtdiDevice.GetRxBytesAvailable(ref written);
            Console.WriteLine("vga written:" + written);
            if (written > 0)
            {
                myFtdiDevice.Read(data_bmp, written, ref written);
            }
            //myFtdiDevice.SetBitMode(0, FTDI.FT_BIT_MODES.FT_BIT_MODE_MPSSE);

            sendCmdNoRes(0x04);

            sendCmdNoRes(0x03);
            sendCmdNoRes(0x02);

            myFtdiDevice.Purge(FTDI.FT_PURGE.FT_PURGE_RX | FTDI.FT_PURGE.FT_PURGE_TX);
        }


        public bool fpga2pcReq()
        {
            return false;
        }


        public bool pc2fpgaReq()
        {
            return false;
        }



        public bool fpga2pcTrans(ref byte[] buff2, ref int xferLen)
        {
            return false;
        }


        public bool pc2fpgaTrans(ref byte[] buff2, ref int xferLen)
        {
            return false;
        }



 
    }
}
